<?php
// defined('IN_YOMI') or Workerman\Protocols\Http::end('Access Denied');

/**
 * Created by PhpStorm.
 * User: Henry Lun
 * Date: 2015/10/21 0021
 * Time: 15:16
 */

/**
 *
 * Class HttpClient
 *
 */
class HttpClient
{
    const LOG_ON = true;

    /**
     * 用于测试
     *
     * http://shanerutter.co.uk/2011/10/30/php-fsockopen-sending-post-data-with-http-request/
     */
    public static function test()
    {
        $fp = fsockopen('localhost', 80);

        $vars = array(
            'hello' => 'world',
            'name' => 'henry',
            'action' => 'test',
        );
        $content = http_build_query($vars);

        fwrite($fp, "POST /hitufwpt/api/user.json HTTP/1.1\r\n");
        fwrite($fp, "Host: localhost\r\n");
        fwrite($fp, "Content-Type: application/x-www-form-urlencoded\r\n");
        fwrite($fp, "Content-Length: " . strlen($content) . "\r\n");
        fwrite($fp, "Connection: close\r\n");
        fwrite($fp, "\r\n");
        fwrite($fp, $content);

        Workerman\Protocols\Http::header('Content-type: text/plain');
        $response = '';
        while (!feof($fp)) {
            $response .= fgets($fp, 1024);
        }
        $response = trim(self::unchunk($response));
        $tmp = explode("\n", $response);
        echo array_pop($tmp);
        Workerman\Protocols\Http::end();
    }

    /**
     * Socket版本(GET请求)
     *
     * @param $request_server
     * @param $request_path
     * @param $request_data
     * @param int $port
     * @param int $timeout
     * @return string
     */
    public static function getBySocket($request_server, $request_path, $request_data = array(), $port = 80, $timeout = 30)
    {
        // create connect
        $socket = @fsockopen($request_server, $port, $errno, $errstr, $timeout);
        if (!$socket) {
            return false;
        }

        // send request
        if (!empty($request_data)) {
            $request_path = $request_path . '?' . http_build_query($request_data);
        }
        $out = "GET {$request_path} HTTP/1.0\r\n";
        $out .= "Host: {$request_server}\r\n";
        $out .= "Connection:close\r\n\r\n";

        fwrite($socket, $out);

        // get response
        $response = '';
        while ($row = fread($socket, 4096)) {
            $response .= $row;
        }

        fclose($socket);

        $pos = strpos($response, "\r\n\r\n");
        $response = substr($response, $pos + 4);

        return $response;
    }

    /**
     * Socket版本(POST请求)
     *
     * HTTP/1.0协议使用非持久连接
     * HTTP/1.1默认使用持久连接
     * 这里HTTP/1.0为什么不能改为HTTP/1.1? 因为Chunked transfer encoding问题: http://www.cnblogs.com/dawnruby/articles/2054492.html
     *
     * @param string $request_server String
     * @param string $request_path
     * @param mixed $request_data
     * @param boolean $is_async 是否进行异步访问
     * @param integer $port
     * @param integer $timeout
     * @param array $cookie_arr
     * @return string
     */
    //
    public static function postBySocket($request_server, $request_path, $request_data, $is_async = false, $port = 80, $timeout = 30, $cookie_arr = array())
    {
        // create connect
        $socket = fsockopen($request_server, $port, $errno, $errstr, $timeout);
        if (!$socket) {
            return false;
        }

        $cookie_str = '';
        foreach ($cookie_arr as $k => $v) {
            if (is_string($v)) {
                $cookie_str .= urlencode($k) . '=' . urlencode($v) . '; ';
            }
        }


        // send request
        $request_string = is_array($request_data) ? http_build_query($request_data) : $request_data;
        $out = "POST {$request_path} HTTP/1.0\r\n";
        $out .= "Host: {$request_server}\r\n";
        $out .= "Accept:*/*\r\n";
        $out .= "User-Agent: " . $_SERVER['HTTP_USER_AGENT'] . "\r\n";
        $out .= "Content-Type: application/x-www-form-urlencoded\r\n";
        $out .= "Content-Length: " . strlen($request_string) . "\r\n";
        if (!empty($cookie_str)) {
            $out .= 'Cookie: ' . substr($cookie_str, 0, -2) . "\r\n";
        }
        $out .= "Connection: close\r\n";
        $out .= "\r\n";
        $out .= $request_string;

        fwrite($socket, $out);

        if (!$is_async) {
            //get response
            $response = '';
            while ($row = fread($socket, 4096)) {
                $response .= $row;
            }
        }

        fclose($socket);

        // Logger::write('$response-before', $response, 'socket.log');

        if (!$is_async) {
            $pos = strpos($response, "\r\n\r\n");
            $response = substr($response, $pos + 4);

            // Logger::write('$response-after', $response, 'socket.log');
            return $response;
        } else {
            return true;
        }
    }

    /**
     * 使用Curl发起请求
     *
     * @param $request_conf array
     * @return mixed
     *
     * http://www.laruence.com/2014/01/21/2939.html
     */
    public static function requestByCurl($request_conf)
    {
        // 10个参数
        $request_url = $request_conf['request_url'];
        $request_params = !empty($request_conf['request_params']) ? $request_conf['request_params'] : "";
        $request_method = !empty($request_conf['request_method']) ? $request_conf['request_method'] : "get";
        $timeout = !empty($request_conf['timeout']) ? $request_conf['timeout'] : "60";
        $content_type = !empty($request_conf['content_type']) ? $request_conf['content_type'] : "application/x-www-form-urlencoded";
        $is_auth = !empty($request_conf['is_auth']) ? $request_conf['is_auth'] : false;
        $auth_name = !empty($request_conf['auth_name']) ? $request_conf['auth_name'] : "";
        $auth_pwd = !empty($request_conf['auth_pwd']) ? $request_conf['auth_pwd'] : "";
        $user_agent = !empty($request_conf['user_agent']) ? $request_conf['user_agent'] : "";
        $cancel_ssl_verified = !empty($request_conf['cancel_ssl_verified']) ? $request_conf['cancel_ssl_verified'] : false;

        if (empty($request_url)) {
            return false;
        }

        if (is_array($request_params)) {
            $request_params = http_build_query($request_params);
        }

        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $request_url);
        curl_setopt($ch, CURLOPT_HEADER, 0); //设置header

        $header = isset($request_conf['is_json_type']) ? array('Content-Length: ' . strlen($request_params), $content_type) : array($content_type);

        // 设置HOST主机头进行访问，很方便的解决了访问外网接口的问题: $header = array("Host: ".$host, 'application/x-www-form-urlencoded');
        // PHP手册里的描述：CURLOPT_HTTPHEADER An array of HTTP header fields to set,
        // in the format array('Content-type: text/plain', 'Content-length: 100')
        curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // 如果需要将结果直接返回到变量里，那加上这句
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
        if (strtolower($request_method) == 'post') {
            curl_setopt($ch, CURLOPT_POST, true);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $request_params);
        }
        if (!empty($request_conf['curlopt_timeout'])) {
            // curl_setopt($ch, CURLOPT_NOSIGNAL, true);    //注意，毫秒超时一定要设置这个
            // curl_setopt($ch, CURLOPT_TIMEOUT_MS, 200); //超时时间200毫秒
            curl_setopt($ch, CURLOPT_TIMEOUT, $request_conf['curlopt_timeout']);
        }
        // 取消SSL验证
        if ($cancel_ssl_verified) {
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        }
        if ($is_auth) {
            // 如果目标URL有$_SERVER['PHP_AUTH_USER']和$_SERVER['PHP_AUTH_PW']的话，必须要加这条语句
            curl_setopt($ch, CURLOPT_USERPWD, $auth_name . ':' . $auth_pwd);
            // curl_setopt($ch, CURLOPT_USERAGENT, $user_agent);
        }
        $response_data = curl_exec($ch);
        $return_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);

        // 日志
        if (self::LOG_ON) {
            $request_conf = array_merge($request_conf, array(
                'return_code' => $return_code,
                'request_url' => $request_url,
                'request_params' => $request_params,
                'request_method' => $request_method,
                'timeout' => $timeout,
                'content_type' => $content_type,
                'is_auth' => $is_auth,
                'auth_name' => $auth_name,
                'auth_pwd' => $auth_pwd,
                'user_agent' => $user_agent,
                'cancel_ssl_verified' => $cancel_ssl_verified,
            ));
            self::curlLogAll($request_conf, $ch, $response_data);
            self::curlLogError($request_conf, $ch, $response_data);
        }

        curl_close($ch);
        return $response_data;
    }

    /**
     * 记录全部curl请求
     *
     * @param $request_conf
     * @param $ch
     * @param $response_data
     */
    public static function curlLogAll($request_conf, $ch, $response_data)
    {
        $log_data = array();
        $log_data['request_side'] = $request_conf;
        $log_data['response_side'] = $response_data;
        $log_data['response_http_code'] = $request_conf['return_code'];
        if ($response_data === false || curl_errno($ch)) {
            $log_data['response_error'] =  'Curl error : ' . curl_error($ch) ;
        }
        OtherUtils::logHelper($log_data, '', $request_conf['log_file']);
    }

    /**
     * 只记录发生错误的curl请求
     *
     * @param $request_conf
     * @param $ch
     * @param $response_data
     */
    public static function curlLogError($request_conf, $ch, $response_data)
    {
        if ($response_data === false || curl_errno($ch)) {
            $log_data = array();
            $log_data['request_side'] = $request_conf;
            $log_data['response_side'] = $response_data;
            $log_data['response_http_code'] = $request_conf['return_code'];
            $log_data['response_error'] =  'Curl error : ' . curl_error($ch) ;
            OtherUtils::logHelper($log_data, '', $request_conf['log_error_file']);
        }
    }


    /**
     * 其它版本
     * 使用方法：
     * $request_data = "app=request&version=beta";
     * requestByOther('http://facebook.cn/restServer.php',$request_data);
     *
     * @param $request_server
     * @param $request_data
     * @return string
     */
    public static function requestByOther($request_server, $request_data)
    {
        $context = array(
            'http' => array(
                'method' => 'POST',
                'header' => 'Content-type: application/x-www-form-urlencoded' .
                    '\r\n' . 'User-Agent : Henry\'s POST Example beta' .
                    '\r\n' . 'Content-length:' . strlen($request_data) + 8,
                'content' => 'mypost=' . $request_data)
        );
        $stream_context = stream_context_create($context);
        $data = file_get_contents($request_server, false, $stream_context);
        return $data;
    }

    /**
     * 处理HTTP 1.1返回时产生的多余字符
     *
     * @param $result
     * @return mixed
     */
    public static function unchunk($result)
    {
        return preg_replace_callback(
            '/(?:(?:\r\n|\n)|^)([0-9A-F]+)(?:\r\n|\n){1,2}(.*?)' .
            '((?:\r\n|\n)(?:[0-9A-F]+(?:\r\n|\n))|$)/si',
            create_function(
                '$matches',
                'return hexdec($matches[1]) == strlen($matches[2]) ? $matches[2] : $matches[0];'
            ),
            $result
        );
    }

}